package app.services;

import app.constant.MemberConstant;
import app.dtos.IntegralType;
import app.kit.TypeKit;
import app.models.member.Account;
import app.models.member.AccountAmount;
import app.models.member.IntegralRecord;
import app.models.member.MemberProduct;
import app.models.member.Points;
import app.models.member.ProfitDetails;
import app.models.order.Order;
import app.models.order.TradeMoney;
import bank.BankService;
import bank.resp.TransferMerchantRspDto;
import com.google.common.base.Optional;
import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.IAtom;
import goja.Logger;
import goja.StringPool;
import org.joda.time.DateTime;

import java.math.BigDecimal;
import java.sql.SQLException;

import static app.Const.FIELD_AMOUNT;
import static app.Const.FIELD_INTEGRAL;
import static app.Const.FIELD_MEMBER;
import static app.Const.FIELD_NAME;
import static app.Const.FIELD_PRODUCT;
import static app.Const.FIELD_STATUS;
import static app.Const.FIELD_TYPE;
import static app.constant.OrderConstant.INC_TYPE;
import static app.constant.OrderConstant.PRODUCTFAIL;
import static app.constant.OrderConstant.TRADE_FAILURE;
import static app.constant.OrderConstant.TRADE_SUCCESS;
import static app.models.member.MemberProduct.FIELD_PLAYMONEY_STATUS;
import static app.models.order.TradeMoney.DAILY_TYPE;
import static app.models.order.TradeMoney.DUE_TYPE;
import static app.models.order.TradeMoney.FAIL_STATUS;
import static app.models.order.TradeMoney.FAIL_TYPE;
import static app.models.order.TradeMoney.MONTH_TYPE;
import static app.models.order.TradeMoney.SUCCESS_STATUS;

/**
 * <p> </p>
 *
 * @author sogYF
 * @version 1.0
 * @since JDK 1.6
 */
public class PlaymoneyService {

    public static final PlaymoneyService me = new PlaymoneyService();


    public void playmoney(final TradeMoney tradeMoney, Optional<Points> optional_points) {

        final int type = TypeKit.getInt(tradeMoney, FIELD_TYPE);

        // 依此进行打款
        // 会员编号
        String memebr_code = tradeMoney.getStr("membercode");
        // 会员产品
        final int member_product = TypeKit.getInt(tradeMoney, "member_product");
        // 打款金额
        final BigDecimal amount = tradeMoney.adjustAmount();

        final int memberId = TypeKit.getInt(tradeMoney, FIELD_MEMBER),
                productId = TypeKit.getInt(tradeMoney, FIELD_PRODUCT);
        String productName = tradeMoney.getStr(FIELD_NAME),
                productCode = tradeMoney.getStr("productcode");
        // 打款时间
        final DateTime now = DateTime.now();

        final MemberProduct memberProduct = new MemberProduct();
        memberProduct.set(StringPool.PK_COLUMN, member_product);

        switch (type) {
            case DUE_TYPE:
            case MONTH_TYPE:
            case DAILY_TYPE: {
                // 收益
                final BigDecimal profit = tradeMoney.getBigDecimal("profit");
                // 本金投资
                final BigDecimal principal = tradeMoney.getBigDecimal("principal");
                //创建收益订单
                final Order order = OrderService.me.buildOrder(memberId, productId, productName, productCode, StringPool.EMPTY, INC_TYPE);
                String orderNo = order.getStr("trade_no");
                order.set("trade_amount", amount);

                Optional<TransferMerchantRspDto> merchantRspDtoOptional =
                        BankService.me.transferMerchantAccnt(now, now, memebr_code, orderNo, amount, "会员收益打款操作");
                if (merchantRspDtoOptional.isPresent()) {
                    TransferMerchantRspDto merchantRspDto = merchantRspDtoOptional.get();
                    if (merchantRspDto.isSuccess()) {
                        // 1.打款成功，更改数据状态
                        tradeMoney.set(FIELD_STATUS, SUCCESS_STATUS);
                        tradeMoney.set("money_time", now);
                        order.set(FIELD_STATUS, TRADE_SUCCESS);

                        memberProduct.set(FIELD_PLAYMONEY_STATUS, MemberConstant.PLAYMONEY_OK);
                        // 创建收益放放纪录
                        final AccountAmount accountAmount;
                        if (BigDecimal.ZERO.compareTo(profit) == 1) {
                            accountAmount = AccountAmount.dao.profitExpense(memberId, productName, now, profit);
                        } else {
                            accountAmount = null;
                        }
                        // 本金退还纪录
                        final AccountAmount paidAmount = AccountAmount.dao.profitPaid(memberId, productName, now, principal);

                        final Account account = AccountService.me.income(profit, principal, memberId);
                        // 2. 发放积分
                        int cost_integral = 0;
                        int balance = 0;
                        if (optional_points.isPresent()) {
                            final Points points = optional_points.get();
                            if (TypeKit.getStatus(points) == 1) {
                                int integral = TypeKit.getInt(points, FIELD_INTEGRAL);
                                int ratio_val = TypeKit.getInt(points, "ratio_val");
                                if (ratio_val > 0) {
                                    // 积分设置大于0 才可以计算赠送积分
                                    // 计算赠送积分
                                    cost_integral = principal.divide(new BigDecimal(ratio_val), 2, BigDecimal.ROUND_HALF_UP)
                                            .multiply(new BigDecimal(integral)).intValue();
                                    final int db_integral = TypeKit.getInt(account, FIELD_INTEGRAL);
                                    balance = (db_integral > 0) ? db_integral + cost_integral : integral;
                                    account.set(FIELD_INTEGRAL, balance);
                                }
                            }
                        }
                        // 更新收益详情为已打款
                        final ProfitDetails profitDetails = ProfitDetails.dao.findByMemberProduct(member_product, memberId, productId);

                        boolean ok = Db.tx(new IAtom() {
                            @Override
                            public boolean run() throws SQLException {
                                boolean ok = order.save()
                                        && paidAmount.save()
                                        && tradeMoney.update()
                                        && account.update()
                                        && memberProduct.update();

                                if (accountAmount != null && ok) {
                                    ok = accountAmount.save();
                                }
                                if (profitDetails != null && ok) {
                                    profitDetails.set(FIELD_STATUS, ProfitDetails.STATUS_OK);
                                    return profitDetails.update();
                                } else {
                                    return ok;
                                }
                            }
                        });
                        if (ok && cost_integral > 0) {
                            IntegralRecord.dao.earning(DateTime.now(), memberId, IntegralType.CASH, "购买产品" + productName + "消费赠送积分", cost_integral, balance);
                        }
                    } else {
                        failPlayMoney(tradeMoney, memberProduct, order, merchantRspDto.getRespMsg());
                    }
                } else {
                    failPlayMoney(tradeMoney, memberProduct, order, "银行接口访问错误");
                }
                break;
            }
            case FAIL_TYPE: {
                //创建募集失败打款订单
                // 实际交易金额
                BigDecimal tradeAmount = tradeMoney.getBigDecimal(FIELD_AMOUNT);
                final Order order = OrderService.me.buildOrder(memberId, productId, productName, productCode, StringPool.EMPTY, PRODUCTFAIL);
                String orderNo = order.getStr("trade_no");
                order.set("trade_amount", amount);

                Optional<TransferMerchantRspDto> merchantRspDtoOptional =
                        BankService.me.transferMerchantAccnt(now, now, memebr_code, orderNo,
                                amount, "产品募集失败，购买产品退款");
                if (merchantRspDtoOptional.isPresent()) {
                    TransferMerchantRspDto merchantRspDto = merchantRspDtoOptional.get();
                    if (merchantRspDto.isSuccess()) {
                        // 1.打款成功，更改数据状态
                        tradeMoney.set(FIELD_STATUS, SUCCESS_STATUS);
                        tradeMoney.set("money_time", now);
                        order.set(FIELD_STATUS, TRADE_SUCCESS);

                        memberProduct.set(FIELD_PLAYMONEY_STATUS, MemberConstant.PLAYMONEY_OK);
                        final AccountAmount accountAmount = AccountAmount.dao.failExpense(memberId, productName, now, tradeAmount);

                        final Account account = AccountService.me.back(tradeAmount, memberId);

                        Db.tx(new IAtom() {
                            @Override
                            public boolean run() throws SQLException {

                                return order.save()
                                        && tradeMoney.update()
                                        && accountAmount.save()
                                        && account.update()
                                        && memberProduct.update();
                            }
                        });

                    } else {
                        failPlayMoney(tradeMoney, memberProduct, order, merchantRspDto.getRespMsg());
                    }
                } else {
                    failPlayMoney(tradeMoney, memberProduct, order, "银行接口访问错误");
                }
            }
        }
    }


    /**
     * 打款失败纪录
     *
     * @param tradeMoney    打款状态
     * @param memberProduct 会员理财产品
     * @param order         订单
     * @param fail_notes    失败原因
     */
    private void failPlayMoney(final TradeMoney tradeMoney, final MemberProduct memberProduct, final Order order, String fail_notes) {
        Logger.error("play profit  money fail");
        tradeMoney.set(FIELD_STATUS, FAIL_STATUS);
        tradeMoney.set("fail_notes", fail_notes);
        order.set(FIELD_STATUS, TRADE_FAILURE);

        memberProduct.set(FIELD_PLAYMONEY_STATUS, MemberConstant.PLAYMONEY_NO);
        Db.tx(new IAtom() {
            @Override
            public boolean run() throws SQLException {
                return order.save() && tradeMoney.update() && memberProduct.update();

            }
        });
    }
}
